package com.yitong.equipment.network.oss

import android.content.Context
import android.os.Handler
import android.text.TextUtils
import com.alibaba.sdk.android.oss.*
import com.alibaba.sdk.android.oss.callback.OSSCompletedCallback
import com.alibaba.sdk.android.oss.common.auth.OSSCredentialProvider
import com.alibaba.sdk.android.oss.common.auth.OSSCustomSignerCredentialProvider
import com.alibaba.sdk.android.oss.model.PutObjectRequest
import com.alibaba.sdk.android.oss.model.PutObjectResult
import com.blankj.utilcode.util.EncryptUtils
import com.blankj.utilcode.util.LogUtils
import com.blankj.utilcode.util.TimeUtils
import okhttp3.*
import org.json.JSONObject
import java.io.IOException
import java.lang.ref.WeakReference
import java.util.*
import kotlin.collections.HashMap


class OSSService {
    private var oss: OSS? = null
    private var contextWeakReference: WeakReference<Context>? = null
    private val handler = Handler()
    fun setContext(context: Context?): OSSService {
        contextWeakReference = WeakReference<Context>(context)
        contextWeakReference!!.get()?.let { initOssConfig(it) }
        return this
    }

    fun reset() {
        if (contextWeakReference != null) {
            contextWeakReference!!.clear()
            contextWeakReference = null
        }
    }

    private fun initOssConfig(context: Context) {
        val credentialProvider: OSSCredentialProvider =
            object : OSSCustomSignerCredentialProvider() {
                override fun signContent(content: String): String {
                    // 您需要在这里依照OSS规定的签名算法，实现加签一串字符内容，并把得到的签名传拼接上AccessKeyId后返回
                    // 一般实现是，将字符内容post到您的业务服务器，然后返回签名
                    // 如果因为某种原因加签失败，描述error信息后，返回nil
                    val params = HashMap<String, Any>()
                    params["content"] = content
                    val builder = FormBody.Builder().add("content", content)

                    val okHttpClient = OkHttpClient()
                    val request: Request =
                        Request.Builder().url(getOssConfig().OSSSignatureEndpoint as String)
                            .header("content-Type", "application/json;charset=UTF-8")
                            .post(builder.build())
                            .build()
                    var result: String? = null
                    try {
                        val response: Response = okHttpClient.newCall(request).execute()
                        if (response.isSuccessful) {
                            val jsonObject = JSONObject(response.body?.string())
                            result = jsonObject.getString("signature")
                        }
                    } catch (e: IOException) {
                        e.printStackTrace()
                    }
                    return result!!
                }
            }
        val conf = ClientConfiguration()
        conf.connectionTimeout = 15 * 1000 // 连接超时，默认15秒
        conf.socketTimeout = 15 * 1000 // socket超时，默认15秒
        conf.maxConcurrentRequest = 5 // 最大并发请求书，默认5个
        conf.maxErrorRetry = 2 // 失败后最大重试次数，默认2次
        oss = OSSClient(context, getOssConfig().OSSEndpoint, credentialProvider, conf)
    }

    /**
     * 上传文件
     *
     * @param uploadInfoList 需要上传的文件(本地path地址)
     * @param resultInfoList 上传成功后地址(远程url地址)
     */
    fun uploadFiles(
        uploadInfoList: List<OSSUploadInfo>?,
        resultInfoList: MutableList<OSSUploadInfo?>?,
        listener: OnUploadListener?
    ) {
        if (oss == null) {
            contextWeakReference?.get()?.let { initOssConfig(it) }
        }
        require(!(uploadInfoList == null || resultInfoList == null)) { "uploadInfoList & resultInfoList can't be empty" }
        if (uploadInfoList.size == resultInfoList.size) {
            LogUtils.d("uploadFiles 上传文件总数 == 已经上传文件总数，全部上传完毕")
            return
        }
        val uploadInfo: OSSUploadInfo = uploadInfoList[resultInfoList.size]
        val localPath: String = uploadInfo.filePath

        val ymd: String = TimeUtils.millis2String(Date().time, "yyyyMMdd")
        if (TextUtils.isEmpty(localPath)) {
            listener?.onUpload(resultInfoList, resultInfoList.size, uploadInfoList.size, false)
            return
        }
        val suffix = localPath.substring(localPath.lastIndexOf(".") + 1) // 截取文件后缀
        val objectKey: String =
            getOssConfig().OSSPrefix.toString() + ymd + "/" + EncryptUtils.encryptMD5File2String(
                localPath
            ) + "." + suffix // OSS上保存的文件地址
        val remoteUrl: String = getOssConfig().OSSBase.toString() + "/" + objectKey // 远程地址
        LogUtils.d("uploadFiles index:" + resultInfoList.size + ", uploadInfo:" + uploadInfo.toString() + ", remoteUrl:" + remoteUrl)
        val exist = doesObjectExist(objectKey) // OSS上是否存在该文件
        val any = if (exist) {
            resultInfoList.add(OSSUploadInfo(uploadInfo.tag, OSSUploadInfo.TYPE_REMOTE, remoteUrl))
            listener!!.onUpload(resultInfoList, resultInfoList.size, uploadInfoList.size, true)
            handler.postDelayed({ uploadFiles(uploadInfoList, resultInfoList, listener) }, 100)
        } else {
            val put = PutObjectRequest(getOssConfig().OSSBucketName, objectKey, localPath)
            put.setProgressCallback { _: PutObjectRequest?, currentSize: Long, totalSize: Long ->
                LogUtils.d(
                    "currentSize: $currentSize totalSize: $totalSize"
                )
            }
            oss?.asyncPutObject(
                put,
                object : OSSCompletedCallback<PutObjectRequest?, PutObjectResult?> {
                    override fun onSuccess(request: PutObjectRequest?, result: PutObjectResult?) {
                        LogUtils.d("uploadFiles onSuccess index:" + resultInfoList.size)
                        handler.post {
                            resultInfoList.add(
                                OSSUploadInfo(
                                    uploadInfo.tag,
                                    OSSUploadInfo.TYPE_REMOTE,
                                    remoteUrl
                                )
                            )
                            listener!!.onUpload(
                                resultInfoList,
                                resultInfoList.size,
                                uploadInfoList.size,
                                true
                            )
                            handler.postDelayed({
                                uploadFiles(
                                    uploadInfoList,
                                    resultInfoList,
                                    listener
                                )
                            }, 100)
                        }
                    }

                    override fun onFailure(
                        request: PutObjectRequest?,
                        clientException: ClientException?,
                        serviceException: ServiceException?
                    ) {
                        LogUtils.d("uploadFiles onFailure index:" + resultInfoList.size)
                        handler.post {
                            listener!!.onUpload(
                                resultInfoList,
                                resultInfoList.size,
                                uploadInfoList.size,
                                false
                            )
                        }
                        var info = ""
                        // 请求异常
                        if (clientException != null) { // 本地异常如网络异常等
                            clientException.printStackTrace()
                            info = clientException.toString()
                        }
                        if (serviceException != null) { // 服务异常
                            LogUtils.e("ErrorCode", serviceException.getErrorCode())
                            LogUtils.e("RequestId", serviceException.getRequestId())
                            LogUtils.e("HostId", serviceException.getHostId())
                            LogUtils.e("RawMessage", serviceException.getRawMessage())
                            info = serviceException.toString()
                        }
                        LogUtils.d("uploadFiles onFailure info:$info")
                    }
                })
        }
    }

    private fun doesObjectExist(objectKey: String): Boolean {
        return try {
            oss!!.doesObjectExist(getOssConfig().OSSBucketName, objectKey)
        } catch (e: ClientException) {
            e.printStackTrace()
            false
        } catch (e: ServiceException) {
            e.printStackTrace()
            false
        }
    }


    interface OnUploadListener {
        fun onUpload(
            resultList: List<OSSUploadInfo?>?,
            currentIndex: Int,
            totalSize: Int,
            isSuccess: Boolean
        )
    }

    private object InnerClass {
        val instance = OSSService()
    }

    companion object {
        val instance: OSSService
            get() = InnerClass.instance
    }
}